// gmarea.cpp : Contains the gamemonkey code for interfacing with the area class
//$Id: gmArea.cpp 25 2009-08-21 02:02:10Z brwarner2 $
#include "stdafx.h"
#include "gmArea.h"
#include "gamemonkey.h"
#include "ObjectManager.h"
#include "Area.h"

namespace gmArea
{
	//Returns an object table
	int GM_CDECL GetObject( gmThread* a_thread )
	{
		GM_CHECK_NUM_PARAMS(1);
		GM_CHECK_STRING_PARAM( name, 0 );

		//Get the this parameter
		gmAreaObject* _this = reinterpret_cast<gmAreaObject*>(a_thread->GetThis()->GetUserSafe(GM_AREA));
		if(!_this) return GM_EXCEPTION;

		//Get the area
		CArea* pArea = _this->pArea;
		ObjectID objectID = pArea->GetByName(name);

		//Push the object onto the stack
		GetGlobalObjectManager(*a_thread->GetMachine())->PushObject(a_thread, objectID);
		return GM_OK;
	}

	int GM_CDECL GetPlayerID( gmThread* a_thread )
	{
		GM_CHECK_NUM_PARAMS(0);

		//Get this
		gmAreaObject* _this = reinterpret_cast<gmAreaObject*>(a_thread->GetThis()->GetUserSafe(GM_AREA));
		if(!_this) return GM_EXCEPTION;

		a_thread->PushInt((int)_this->pArea->GetActivePlayerID());
		return GM_OK;
	}

	//Get dot operator
	void GM_CDECL GetDot( gmThread* a_thread, gmVariable* a_operands )
	{
		//Get area object
		gmAreaObject* pObj = reinterpret_cast<gmAreaObject*>(a_operands[0].GetUserSafe(GM_AREA));
		GM_ASSERT( pObj != 0 );

		//Set return
		a_operands[0] = pObj->pTable->Get(a_operands[1]);
	}

	int GM_CDECL SetPlayerArea( gmThread* a_thread )
	{
		GM_CHECK_NUM_PARAMS(1);
		GM_CHECK_INT_PARAM( playerID, 0 );

		//Check for valid number
		if( playerID < 0 || playerID > 3 )
		{
			a_thread->GetMachine()->GetLog().LogEntry("Error: Player ID %d is out of range of valid players", playerID);
			return GM_EXCEPTION;
		}

		//Get the this parameter
		gmAreaObject* _this = reinterpret_cast<gmAreaObject*>(a_thread->GetThis()->GetUserSafe(GM_AREA));
		if(!_this) return GM_EXCEPTION;

		//Set the player ID
		_this->pArea->SetPlayerArea(playerID);
		return GM_OK;
	}

	gmUserObject* CreateAreaObject( gmMachine* pGM, CArea* pArea )
	{
		//Create a new area object
		gmAreaObject* pObj = new gmAreaObject();
		pObj->pArea = pArea;
		pObj->pTable = pGM->AllocTableObject();

		//Add functions to the table
		pObj->pTable->Set(pGM, "GetObject", gmVariable(pGM->AllocFunctionObject(GetObject)));
		pObj->pTable->Set(pGM, "GetActivePlayer", gmVariable(pGM->AllocFunctionObject(GetPlayerID)));
		pObj->pTable->Set(pGM, "SetPlayerArea", gmVariable(pGM->AllocFunctionObject(SetPlayerArea)));

		//Create the user object
		gmUserObject* pUser = pGM->AllocUserObject(pObj, GM_AREA);

		//Make sure it is kept safe
		pGM->AddCPPOwnedGMObject(pUser);
		return pUser;
	}

	void DestroyAreaObject( gmMachine* pGM, gmUserObject* pUser )
	{
		//Remove it from the safe types
		pGM->RemoveCPPOwnedGMObject(pUser);
	}

	void GM_CDECL AsString( gmUserObject* pUser, char* buffer, int length )
	{
		const char* str = "Area Object";
		unsigned int copylen = std::min<unsigned int>(length, 12);
		memcpy( buffer, str, copylen );
	}

	void RegisterLibrary(gmMachine* pGM)
	{
		//Register type
		GM_AREA = pGM->CreateUserType("Area");
		pGM->RegisterTypeOperator(GM_AREA, O_GETDOT, 0, GetDot);

		// Register garbage collection etc. for our new type
		#if GM_USE_INCGC
		  pGM->RegisterUserCallbacks(GM_AREA, 
			  GC::GCTrace, GC::GCDestruct, 
										   AsString); 
		#else //GM_USE_INCGC  
			pGM->RegisterUserCallbacks(GM_AREA, 
										   GC::GameObjectMark, GC::GameObjectGC, 
										   AsString); 
		#endif //GM_USE_INCGC
	}

	namespace GC {
		#if GM_USE_INCGC
		bool GM_CDECL GCTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* 
		a_gc, const int a_workLeftToDo, int& a_workDone)
		{
		  GM_ASSERT(a_object->m_userType == GM_AREA);
		  gmAreaObject * object = (gmAreaObject *) a_object->m_user;
		  if(object->pTable) a_gc->GetNextObject(object->pTable);
		  a_workDone += 2; // contents + this
		  return true;
		}
		void GM_CDECL GCDestruct(gmMachine * a_machine, gmUserObject* a_object)
		{
		  GM_ASSERT(a_object->m_userType == GM_AREA);
		  gmAreaObject * object = (gmAreaObject *) a_object->m_user;
		  object->pTable = NULL;
		}
		#else //GM_USE_INCGC
		// Garbage collect 'Mark' function
		void GM_CDECL GameObjectMark(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
		{
		  GM_ASSERT(a_object->m_userType == GM_AREA);
		  gmAreaObject * object = (gmAreaObject *) a_object->m_user;
		  object->pTable->Mark(a_machine, a_mark);
		}
		// Garbage collect 'Garbage Collect' function
		void GM_CDECL GameObjectGC(gmMachine * a_machine, gmUserObject * a_object, gmuint32 a_mark)
		{
		  GM_ASSERT(a_object->m_userType == GM_AREA);
		  gmAreaObject * object = (gmAreaObject *) a_object->m_user;
		  object->pTable.Destruct(a_machine);
		  delete object;
		}
		#endif //GM_USE_INCGC
	}
}